-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat] 댓글 기능 구현 #232
base: develop
Are you sure you want to change the base?
[Feat] 댓글 기능 구현 #232
Conversation
d1d6cc6
to
b66dea7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
커서 페이지네이션을 이렇게도 구현할수 있구나라는 생각에 무릎을 탁 치고 갑니닷!!!!
몇개 궁금한거 있어서 코멘트 남겼는데 확인해주심 감사하겠슴다🫡
if (isNotCommentOwner(findComment, findUser, findNotice)) { | ||
throw new NotFoundException(ErrorCode.COMMENT_NOT_FOUND); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOT_FOUND 보다는 403에러를 반환하는 에러코드(Ex. NO_PERMISSION
나 COMMENT_ACCESS_DENIED
과같은..?)를 추가로 만드는게 더 의미를 잘 표현할 수 있지 않을가 생각합니닷
|
||
import java.util.Objects; | ||
|
||
@Slf4j |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
활용되지 않는 어노테이션인거 같아유
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앗... 로그 남긴다는게 까먹었네요.... 수신~
private final NoticeCommentWritingUseCase noticeCommentWritingUseCase; | ||
private final NoticeCommentEditingUseCase noticeCommentEditingUseCase; | ||
private final NoticeCommentDeletingUseCase noticeCommentDeletingUseCase; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
하나의 CommandUseCase로 묶지 않고 Create, Update, Delete를 구분한 이유가 궁금함니닷
(가독성과 관리가 편해서인가 싶은 생각이 들었슴니당)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사실 CommandUseCase 는 너무 뭉둥그려진 유스케이스에 해당합니다. 거의 모든 유스케이스를 CommandUseCase 와 QueryUseCase로 이분법적으로 나눌 수 있을탠데... 이러면 인터페이스의 명세라는 의미가 사실 조금 사라진다 생각합니다.
가급적 유스케이스도 분리하여 의미를 전달해주는 것 이 좋아요, 다만 그럼 이전에는 왜? CommandUseCase 로 했냐? 라고 물어본다면, 그건 그당시 기존 레이어드 아키텍처에서 헥사고날로 전환하면서... 작업량이 많아... 일단 하나로 퉁쳤습니다...
언젠가 기존의 유스케이스도 명확하게 분리해야지라고 생각 중입나다~
public ResponseEntity<BaseResponse> createComment( | ||
@Parameter(description = "공지 ID") @PathVariable("id") Long id, | ||
@RequestHeader(USER_TOKEN_HEADER_KEY) String userToken, | ||
@RequestBody NoticeCommentCreateRequest request | ||
) { | ||
if (request.parentId() == null) { | ||
var command = new WriteCommentCommand( | ||
userToken, | ||
id, | ||
request.content() | ||
); | ||
|
||
noticeCommentWritingUseCase.process(command); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var가 자바에도 있다는건 알고 있었는데 실제로 보니 상당히 코틀린같으면서도 자바같은 요 느낌....ㅋㅋㅋㅎㅎㅋㅋㅎ
이걸 보니 값을 객체로 변환하는 과정에서만큼은 굳이 변수 선언부에 객체이름을 다 적지 않아도 충분하다는 것을 느끼게 되네유
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ㅋㅋㅋ 사실 맘같아서는 코틀린 쓰고 싶은데... 그다음 서버 학생분 물려드릴거 생각하여... 자바로 유지중...
*/ | ||
CursorBasedList<CommentAndSubCommentsResult> findComments(Long noticeId, String cursor, int size); | ||
|
||
record ReadCommentsCommand( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 이건 언제쓰는 커맨드인가유??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ㅋㅋㅋㅋ 만들고 사용을 안했군요... 확인 감사합니다~
public CursorBasedList<CommentAndSubCommentsResult> findComments(Long noticeId, String cursor, int size) { | ||
return CursorBasedList.of( | ||
Math.min(size, MAX_COMMENT_QUERY_SIZE), | ||
it -> it.comment().id().toString(), | ||
searchSize -> { | ||
List<CommentReadModel> parentComments = commentQueryPort | ||
.findExcludeSubCommentByCursor(noticeId, cursor, searchSize); | ||
|
||
Set<Long> parentCommentIds = parentComments.stream() | ||
.map(CommentReadModel::getId) | ||
.collect(Collectors.toSet()); | ||
|
||
List<CommentReadModel> subComments = commentQueryPort.findSubCommentByIds(noticeId, parentCommentIds); | ||
|
||
// 부모 댓글을 Key로 하고, 그에 해당하는 자식 댓글 리스트를 값으로 가지는 Map 생성 | ||
Map<Long, List<CommentDetailResponse>> parentToSubCommentsMap = subComments.stream() | ||
.filter(subComment -> subComment.getParentId() != null) | ||
.map(CommentDetailResponse::of) | ||
.collect(Collectors.groupingBy(CommentDetailResponse::parentId)); | ||
|
||
// 부모 댓글과 자식 댓글을 조합하여 CommentAndSubCommentsResult 리스트 생성 | ||
List<CommentAndSubCommentsResult> commentResults = parentComments.stream() | ||
.map(parent -> { | ||
List<CommentDetailResponse> subCommentList = parentToSubCommentsMap | ||
.getOrDefault(parent.getId(), Collections.emptyList()); | ||
|
||
// 부모 댓글이 삭제되었고, 답글이 없는 경우 제외 | ||
if (parent.getDestroyedAt() != null && subCommentList.isEmpty()) { | ||
return null; | ||
} | ||
|
||
return new CommentAndSubCommentsResult(CommentDetailResponse.of(parent), subCommentList); | ||
}) | ||
.filter(Objects::nonNull) | ||
.toList(); | ||
|
||
return commentResults.subList(START_INDEX, Math.min(searchSize, commentResults.size())); | ||
} | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WoW🤩
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
연산량이 많은 부분이라 메서드로 추출 안했습니다~
그냥 하나의 findComments() 안에서 다 보는것이 좋을것 같아서요~
public class Cursor { | ||
|
||
private final String stringCursor; | ||
|
||
public Cursor(String stringCursor) { | ||
this.stringCursor = stringCursor; | ||
} | ||
|
||
public String getStringCursor() { | ||
return stringCursor; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
혹시 이 커서 객체는 어디에 쓰는건가유??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ㅋㅋㅋㅋ 이것도... 만들고 사용을 안했군요... 확인 감사합니다~
원래 Controller에서 받을때 한번 감싸서 전달했어야 해요~
|
TODO (진행중)
추가로, 로그인이 어떻게 될지 몰라서? 일단 TOKEN으로 로그인 한다 생각하고 처리하였습니다.
로그인 구현이 완료되면 해당 부분 알맞게 변경해주세요~